typedef void (*jitpy)(void);

tree *selfid;
label err;


typedef struct _final{
  asdl_seq *body;
  label *brk, cnt;
  struct _final *fin;
} final;
bop compilebinop[] = {-1, badd, bsub, -1, -1, -1, -1, -1, -1, bor, bxor, band, -1};
cond compilecc[] = {0, cce, ccne, ccl, ccle, ccg, ccge};
cond compileinvcc[] = {0, ccne, cce, ccge, ccg, ccle, ccl};

inline void alloc(int size){
  label l;
  getd((int) &nextfreeinheap, eax);
  movr(ebx, eax);
  addimm(ebx, 4*size);
  getd((int) &endvmheap, ecx);
  cmpr(ebx, ecx);
  l = jccu(ccle);
  callimm((int) &gcStopAndCopy);
  getd((int) &nextfreeinheap, eax);
  movr(ebx, eax);
  addimm(ebx, 4*size);
  setlbl(l);
  setd((int) &nextfreeinheap, ebx);
}

void compile_target(expr_ty e, arraylist *local, int x){
  int i;
  switch(e->kind){
    /*
  case Attribute_kind:
    compile_exp(e->v.Attribyte.value, local, x);
    if(self && e->value->kind == Name_kind && lookup(e->value->id) == selfid){
      self = add_env(self, e->v.Attribute.attr);
    }
    break;*/
  case Name_kind:
    setlocal(x + arraylist_search(local, lookupid(e->v.Name.id)), eax);
    break;
    /*
  case List_kind:
    for(i = 0 ; i < e->v.List.elts->size ; i++)
      compile_target(e->v.List.elts->elements[i], local, self, x);
    break;
    */
  case Tuple_kind:
    cmpattrimm(eax, 0, (int) &tupletype);
    jccl(ccne, err);
    cmpattrimm(eax, 1, e->v.Tuple.elts->size);
    jccl(ccne, err);
    pushr(eax);
    for(i = 0 ; i < e->v.Tuple.elts->size ; i++){
      getattr(eax, 2+i, eax);
      compile_target(e->v.Tuple.elts->elements[i], local, x+1);
      getlocal(0, eax);
    }
    popr(eax);
    break;
  default:;
  }
}

void compile_exp(expr_ty e, arraylist *local, int x){
  int i, j;
  label l, l0, l1;
   switch(e->kind){
  case BoolOp_kind:
    compile_exp(e->v.BoolOp.values->elements[0], local, x);
    {
      cond cc = e->v.BoolOp.op == And ? cce : ccne;
      label ls[e->v.BoolOp.values->size-1];
      for(i = 1 ; i < e->v.BoolOp.values->size ; i++){
	cmpattrimm(eax, 0, (int) &inttype);
	jccl(ccne, err);
	getattr(eax, 1, ebx);
	cmpimm(ebx, 0);
	ls[i-1] = jccu(cc);
	compile_exp(e->v.BoolOp.values->elements[i], local, x);
      }
      i--;
      while(i){
	setlbl(ls[--i]);
      }
    }
    break;
  case BinOp_kind:
    compile_exp(e->v.BinOp.left, local, x);
    pushr(eax);
    compile_exp(e->v.BinOp.right, local, x+1);
    getlocal(0, edx);
    getattr(eax, 0, ecx);
    cmpimm(ecx, (int) &inttype);
    l0 = jccu(cce);
    cmpimm(ecx, (int) &booltype);
    jccl(ccne, err);
    setlbl(l0);
    getattr(edx, 0, ecx);
    cmpimm(ecx, (int) &inttype);
    l0 = jccu(cce);
    cmpimm(ecx, (int) &booltype);
    jccl(ccne, err);
    setlbl(l0);
    pushr(eax);
    alloc(2);
    movr(ebx, eax);
    popr(ecx);
    getattr(ecx, 1, ecx);
    popr(eax);
    getattr(eax, 1, eax);
    setattrimm(ebx, 0, (int) &inttype);
    i = eax;
    switch(e->v.BinOp.op){
    case Add:
    case Sub:
    case BitOr:
    case BitAnd:
    case BitXor:
      binop(compilebinop[e->v.BinOp.op], eax, ecx);
      break;
    case Mult:
      bincop(bmul, ecx);
      break;
    case Div:
    case FloorDiv:
      pushimm(0);
      popr(edx);
      bincop(bdiv, ecx);
      break;
    case Mod:
      pushimm(0);
      popr(edx);
      bincop(bmod, ecx);
      i = edx;
      break;
    case LShift:
      binsop(bsal, eax);
      break;
    case RShift:
      binsop(bsar, eax);
      break;
    default:;
    }
    setattr(ebx, 1, i);
    movr(eax, ebx);
    break;
  case UnaryOp_kind:
    //compile_exp(e->v.UnaryOp.operand, local, x);
    //unaryop(e->v.UnaryOp.op);
    break;
  case Lambda_kind:
    break;
  case Dict_kind:
    break;
  case ListComp_kind:
    l1 = getlbl();
    {
      comprehension_ty com = e->v.ListComp.generators->elements[0];
      compile_exp(com->iter, local, x);
      compile_target(com->target, local, x);
      cmpimm(eax, 0);
      l = jccu(cce);
      for(j = 0 ; j < com->ifs->size ; j++){
	compile_exp(com->ifs->elements[j], local, x);
	cmpimm(eax, 0);
	jccl(cce, l1);
      }
    }
    for(i = 1 ; i < e->v.ListComp.generators->size ; i++){
      comprehension_ty com = e->v.ListComp.generators->elements[i];
      l0 = l1;
      l1 = getlbl();
      compile_exp(com->iter, local, x);
      compile_target(com->target, local, x);
      cmpimm(eax, 0);
      jccl(cce, l0);
      for(j = 0 ; j < com->ifs->size ; j++){
	compile_exp(com->ifs->elements[j], local, x);
	cmpimm(eax, 0);
	jccl(cce, l1);
      }
    }
    compile_exp(e->v.ListComp.elt, local, x);
    jmpl(l1);
    setlbl(l);
    break;
  case GeneratorExp_kind:
    break;
  case Yield_kind:
    break;
  case Compare_kind:
    compile_exp(e->v.Compare.left, local, x);
    {
      label ls[e->v.Compare.ops->size];
      for(i = 0 ; i < e->v.Compare.ops->size ; i++){
	pushr(eax);
	compile_exp(e->v.Compare.comparators->elements[i], local, x+1);
	cmpattrimm(eax, 0, (int) &inttype);
	jccl(ccne, err);
	getattr(eax, 1, ecx);
	popr(ebx);
	cmpattrimm(ebx, 0, (int) &inttype);
	jccl(ccne, err);
	cmpattr(ebx, 1, ecx);
	ls[i] = jccu(compileinvcc[(cmpop_ty) e->v.Compare.ops->elements[i]]);
      }
      alloc(2);
      setattrimm(eax, 0, (int) &booltype);
      setattrimm(eax, 1, true);
      l = jmpu();
      while(i)
	setlbl(ls[--i]);
    }
    alloc(2);
    setattrimm(eax, 0, (int) &booltype);
    setattrimm(eax, 1, false);
    setlbl(l);
    break;
  case Call_kind:
    compile_exp(e->v.Call.func, local, x);
    pushr(eax);
    for(i = 0 ; e->v.Call.args && i < e->v.Call.args->size ; i++){
      compile_exp(e->v.Call.args->elements[i], local, x+1+i);
      pushr(eax);
    }
    getlocal(i, eax);
    cmpattrimm(eax, 0, (int) &funtype);
    jccl(ccne, err);
    callattr(eax, 1);
    addimm(esp, 4*i + 4);
    break;
  case Repr_kind:
    break;
  case Num_kind:
     alloc(2);
    setattrimm(eax, 0, (int) &inttype);
    setattrimm(eax, 1, PyInt_Value(e->v.Num.n));
    break;
  case Str_kind:
    alloc(2);
    setattrimm(eax, 0, (int) &stringtype);
    setattrimm(eax, 1, (int) PyString_AsString(e->v.Str.s));
    break;
  case Attribute_kind:
    //compile_exp(e->v.Attribute.value, local, x);
    break;
  case Subscript_kind:
    compile_exp(e->v.Subscript.value, local, x);
    pushr(eax);
    switch(e->v.Subscript.slice->kind){
    case Ellipsis_kind:
      break;
    case Slice_kind:
      /*
      compile_exp(e->v.Subscript.slice->v.Slice.lower, local, x++);
      pushr(eax);
      compile_exp(e->v.Subscript.slice->v.Slice.upper, local, x++);
      pushr(eax);
      if(e->v.Subscript.slice->v.Slice.step){
	compile_exp(e->v.Subscript.slice->v.Slice.step, local, x++);
	pushr(eax);
	}*/
      break;
    case ExtSlice_kind:
      break;
    case Index_kind:
      compile_exp(e->v.Subscript.slice->v.Index.value, local, x+1);
      cmpattrimm(eax, 0, (int) &inttype);
      jccl(ccne, err);
      getattr(eax, 1, ebx);
      popr(eax);
      cmpattrimm(eax, 0, (int) &tupletype);
      jccl(ccne, err);
      cmpimm(ebx, 0);
      jccl(ccl, err);
      cmpattr(eax, 1, ebx);
      jccl(ccle, err);
      getarray(eax, ebx, eax);
      break;
    }
    break;
  case Name_kind:
    getlocal(x + arraylist_search(local, lookupid(e->v.Name.id)), eax);
    cmpimm(eax, 0);
    jccl(cce, err);
    break;
  case List_kind:
    break;
  case Tuple_kind:
    for(i = 0 ; i < e->v.Tuple.elts->size ; i++){
      compile_exp(e->v.Tuple.elts->elements[i], local, x++);
      pushr(eax);
    }
    alloc(2+e->v.Tuple.elts->size);
    setattrimm(eax, 0, (int) &tupletype);
    setattrimm(eax, 1, e->v.Tuple.elts->size);
    while(i--)
      popattr(eax, i+2);
    break;
  }
}

void compile_stmt(stmt_ty s, arraylist *local, final *fin, label *brk, label cnt, int x){
  int i, j;
  label l, l0, l1, lb = NULL;
  switch(s->kind){
  case FunctionDef_kind:
    alloc(2);
    setattrimm(eax, 0, (int) &funtype);
    setattrimm(eax, 1, (int) s->v.FunctionDef.body);
    setlocal(x + arraylist_search(local, lookupid(s->v.FunctionDef.name)), eax);
    break;
  case ClassDef_kind:
    callimm((int) s->v.ClassDef.body);
    setlocal(x + arraylist_search(local, lookupid(s->v.ClassDef.name)), eax);
    break;
  case Return_kind:
    while(fin){
      for(i = 0 ; i < fin->body->size ; i++)
	compile_stmt(fin->body->elements[i], local, fin->fin, fin->brk, fin->cnt, x);
      fin = fin->fin;
    }
    addimm(esp, 4*(local->size + x));
    ret();
  case Delete_kind:
    break;
  case AugAssign_kind:
    if(s->v.AugAssign.target->kind == Attribute_kind){
      /*compile_exp(s->v.AugAssign.target->v.Attribute.value, local, x);
      pushr(eax);
      compile_exp(s->v.Assign.value, local, x);
      popr(ebx);
      //e->v.AugAssign.target->v.Attribute->id*/
    }else{
      asdl_seq aseq = {1, 0, {&s->v.AugAssign.target}};
      struct _stmt ass = {Assign_kind, {Assign: {&aseq, s->v.AugAssign.value}}};
      compile_stmt(&ass, local, fin, brk, cnt, x);
    }
    break;
  case Assign_kind:
    compile_exp(s->v.Assign.value, local, x);
    if(s->v.Assign.targets->size > 1){
      struct _expr target = {Tuple_kind, {Tuple: {s->v.Assign.targets, Store}}};
      compile_target(&target, local, x);
    }else{
      compile_target(s->v.Assign.targets->elements[0], local, x);
    }
    break;
  case Print_kind:
    for(i = 0 ; i < s->v.Print.values->size ; i++){
      compile_exp(s->v.Print.values->elements[i], local, x + i);
      pushr(eax);
    }
    pushimm(s->v.Print.values->size);
    pushimm(s->v.Print.nl);
    callimm((int) &pyprint);
    addimm(esp, 4*s->v.Print.values->size+8);
    break;
  case For_kind:
    compile_exp(s->v.For.iter, local, x);
    cmpattrimm(eax, 0, (int) &tupletype);
    jccl(ccne, err);
    pushr(eax);
    pushimm(-1);
    popr(ebx);
    l = getlbl(); //continue
    popr(ebx);
    addimm(ebx, 1);
    getlocal(0, eax);
    cmpattr(eax, 1, ebx);
    l0 = jccu(cce);
    pushr(ebx);
    getarray(eax, ebx, eax);
    compile_target(s->v.For.target, local, x + 2);
    for(i = 0 ; i < s->v.For.body->size ; i++)
      compile_stmt(s->v.For.body->elements[i], local, fin, &lb, l, x + 2);
    jmpl(l);
    setlbl(l0);
    addimm(esp, 4);
    if(s->v.For.orelse)
      for(i = 0 ; i < s->v.For.orelse->size ; i++)
	compile_stmt(s->v.For.orelse->elements[i], local, fin, brk, cnt, x);
    while(lb){
      l0 = lb;
      lb = *((label *) lb);
      setlbl(l0);
    }
    break;
  case While_kind:
    l = getlbl(); //continue
    compile_exp(s->v.While.test, local, x);
    getattr(eax, 0, ebx);
    cmpimm(ebx, (int) &inttype);
    l0 = jccu(cce);
    cmpimm(ebx, (int) &booltype);
    l1 = jccu(ccne);
    setlbl(l0);
    cmpattrimm(eax, 1, 0);
    l0 = jccu(cce);
    setlbl(l1);
    for(i = 0 ; i < s->v.While.body->size ; i++)
      compile_stmt(s->v.While.body->elements[i], local, fin, &l1, l, x);
    jmpl(l);
    setlbl(l0);
    if(s->v.While.orelse)
      for(i = 0 ; i < s->v.While.orelse->size ; i++)
	compile_stmt(s->v.While.orelse->elements[i], local, fin, brk, cnt, x);
    while(lb){
      l0 = lb;
      lb = *((label *) lb);
      setlbl(l0);
    }
    break;
  case If_kind:
    compile_exp(s->v.If.test, local, x);
    getattr(eax, 0, ebx);
    cmpimm(ebx, (int) &inttype);
    l0 = jccu(cce);
    cmpimm(ebx, (int) &booltype);
    l1 = jccu(ccne);
    setlbl(l0);
    cmpattrimm(eax, 1, 0);
    l0 = jccu(cce);
    setlbl(l1);
    for(i = 0 ; i < s->v.If.body->size ; i++)
      compile_stmt(s->v.If.body->elements[i], local, fin, brk, cnt, x);
    if(s->v.If.orelse){
      l1 = jmpu();
      setlbl(l0);
      for(i = 0 ; i < s->v.If.orelse->size ; i++){
	compile_stmt(s->v.If.orelse->elements[i], local, fin, brk, cnt, x);
      }
      setlbl(l1);
    }else
      setlbl(l0);
    break;
  case Raise_kind:
    compile_exp(s->v.Raise.type, local, x);
    movr(esp, ebp);
    popr(ebp);
    ret();
    break;
  case TryExcept_kind:
    l = pushu();
    pushr(ebp);
    movr(ebp, esp);
    for(i = 0 ; i < s->v.TryExcept.body->size ; i++){
      compile_stmt(s->v.TryExcept.body->elements[i], local, fin, brk, cnt, x);
    }
    movr(esp, ebp);
    popr(ebp);
    addimm(esp, 4);
    setlbl(l);
    {
      label ls[s->v.TryExcept.handlers->size];
      for(i = 0 ; i < s->v.TryExcept.handlers->size ; i++){
	excepthandler_ty ex = s->v.TryExcept.handlers->elements[i];
	compile_exp(ex->type, local, x);
	//instance
	l = jccu(ccne);
	if(ex->name)
	  compile_target(ex->name, local, x);
	for(j = 0 ; j < ex->body->size ; j++)
	  compile_stmt(ex->body->elements[j], local, fin, brk, cnt, x);
	ls[i] = jmpu();
	setlbl(l);
      }
      if(s->v.TryExcept.orelse)
	for(i = 0 ; i < s->v.TryExcept.orelse->size ; i++)
	  compile_stmt(s->v.TryExcept.orelse->elements[i], local, fin, brk, cnt, x);
      while(i)
	setlbl(ls[--i]);
    }
    break;
  case TryFinally_kind:
    l = pushu();
    pushr(ebp);
    movr(ebp, esp);
    {
      final f = {s->v.TryFinally.finalbody, brk, cnt, fin};
      for(i = 0 ; i < s->v.TryFinally.body->size ; i++)
	compile_stmt(s->v.TryFinally.body->elements[i], local, &f, brk, cnt, x);
    }
    movr(esp, ebp);
    popr(ebp);
    addimm(esp, 4);
    pushimm(0);
    popr(eax);
    setlbl(l);
    for(i = 0 ; i < s->v.TryFinally.finalbody->size ; i++)
      compile_stmt(s->v.TryFinally.finalbody->elements[i], local, fin, brk, cnt, x);
    cmpimm(eax, 0);
    l = jccu(cce);
    movr(esp, ebp);
    popr(ebp);
    ret();
    setlbl(l);
    break;
  case Assert_kind:
    break;
  case Import_kind:
    break;
  case ImportFrom_kind:
    break;
  case Exec_kind:
    break;
  case Global_kind:
    break;
  case Expr_kind:
    compile_exp(s->v.Expr.value, local, x);
    break;
  case Pass_kind:
    break;
  case Break_kind:
    if(brk){
      while(fin && fin->cnt == cnt){
	for(i = 0 ; i < fin->body->size ; i++)
	  compile_stmt(fin->body->elements[i], local, fin->fin, brk, cnt, x);
	fin = fin->fin;
      }
      l0 = jmpu();
      *((label *) l0) = *brk;
      *brk = l0;
      break;
    }
  case Continue_kind:
    if(cnt){
      while(fin && fin->cnt == cnt){
	for(i = 0 ; i < fin->body->size ; i++)
	  compile_stmt(fin->body->elements[i], local, fin->fin, brk, cnt, x);
	fin = fin->fin;
      }
      jmpl(cnt);
      break;
    }
  default:;
  }
}


void check_target(expr_ty e, environment **local, environment **self){
  int i;
  switch(e->kind){
  case Attribute_kind:
    if(self && e->v.Attribute.value->kind == Name_kind && lookupid(e->v.Attribute.value->v.Name.id) == selfid){
      add_env(self, e->v.Attribute.attr);
    }
    break;
  case Name_kind:
    add_env(local, e->v.Name.id);
    break;
  case List_kind:
    for(i = 0 ; i < e->v.List.elts->size ; i++)
      check_target(e->v.List.elts->elements[i], local, self);
    break;
  case Tuple_kind:
    for(i = 0 ; i < e->v.Tuple.elts->size ; i++)
      check_target(e->v.Tuple.elts->elements[i], local, self);
    break;
  default:;
  }
}

void check_stmt(stmt_ty s, environment **local, environment **self){
  int i;
  environment *new_local = NULL, *new_self = NULL;
  arraylist *snewloc;
  label code;
  switch(s->kind){
  case FunctionDef_kind:
    add_env(local, s->v.FunctionDef.name);
    /*
    if(s->v.FunctionDef.args->args){
      for(i = 0 ; i < s->v.FunctionDef.args->args->size ; i++)
	check_target(s->v.FunctionDef.args->args->elements[i], &new_local, self);
      expr_ty t = s->v.FunctionDef.args->args->elements[0];
      if(t->kind == Name_kind && lookupid(t->v.Name.id) != selfid)
	self = &new_self; //ignore
    }else self = &new_self; //ignore
    if(s->v.FunctionDef.args->vararg)
      add_local(&new_local, s->v.FunctionDef.args->vararg);
    if(s->v.FunctionDef.args->kwarg)
    add_local(&new_local, s->v.FunctionDef.args->kwarg);
    */
    //add_local(&new_local, ""); //pystr
    for(i = 0; i < s->v.FunctionDef.body->size; i++)
      check_stmt(s->v.FunctionDef.body->elements[i], &new_local, self);
    code = getlbl();
    snewloc = convert_env(new_local);
    for(i = 0 ; i < snewloc->size ; i++) pushimm(0);
    for(i = 0; i < s->v.FunctionDef.body->size; i++)
      compile_stmt(s->v.FunctionDef.body->elements[i], snewloc, NULL, NULL, 0, 0); //&new_local
    pushimm(1);
    popr(eax);
    addimm(esp, 4*snewloc->size);
    ret();
    s->v.FunctionDef.body = (void *) code; //save the code pointer in the AST
    break;
  case ClassDef_kind:
    add_env(local, s->v.ClassDef.name);
    for(i = 0; i < s->v.ClassDef.body->size; i++)
      check_stmt(s->v.ClassDef.body->elements[i], &new_local, &new_self);
    break;
  case Assign_kind:
    for(i = 0 ; i < s->v.Assign.targets->size ; i++)
      check_target(s->v.Assign.targets->elements[i], local, self);
    break;
  case AugAssign_kind:
    check_target(s->v.AugAssign.target, local, self);
    break;
  case For_kind:
    check_target(s->v.For.target, local, self);
    for(i = 0; i < s->v.For.body->size; i++)
      check_stmt(s->v.For.body->elements[i], local, self);
    if(s->v.For.orelse)
      for(i = 0; i < s->v.For.orelse->size; i++)
	check_stmt(s->v.For.orelse->elements[i], local, self);
    break;
  case While_kind:
    for(i = 0; i < s->v.While.body->size; i++)
      check_stmt(s->v.While.body->elements[i], local, self);
    if(s->v.While.orelse)
      for(i = 0; i < s->v.While.orelse->size; i++)
	check_stmt(s->v.While.orelse->elements[i], local, self);
    break;
  case If_kind:
    for(i = 0; i < s->v.If.body->size; i++)
      check_stmt(s->v.If.body->elements[i], local, self);
    if(s->v.If.orelse)
      for(i = 0; i < s->v.If.orelse->size; i++)
	check_stmt(s->v.If.orelse->elements[i], local, self);
    break;
  case TryExcept_kind:
    for(i = 0 ; i < s->v.TryExcept.handlers->size ; i++){
      excepthandler_ty h = s->v.TryExcept.handlers->elements[i];
      check_target(h->name, local, self);
    }
    break;
  case Expr_kind: //lambda
    break;
    /*
  case Import_kind:
    break;
  case ImportFrom_kind:
    break;
  case Exec_kind:
    break;
  case Global_kind:
    break;
    */
  default:;
  }
}

jitpy compile(mod_ty m){
  void *code = getlbl();

  int i;
  environment *global = NULL, *ignore = NULL;
  arraylist *cglobal = NULL;

  selfid = lookup("self");
  err = NULL;

  switch(m->kind){
  case Module_kind:
    for(i = 0 ; i < m->v.Module.body->size ; i++)
      check_stmt(m->v.Module.body->elements[i], &global, &ignore);
    cglobal = convert_env(global);
    code = getlbl();
    setd((int) &stackstart, esp);
    for(i = 0 ; i < cglobal->size ; i++) pushimm(0);
    for(i = 0 ; i < m->v.Module.body->size ; i++)
      compile_stmt(m->v.Module.body->elements[i], cglobal, NULL, NULL, 0, 0);
    break;
  case Interactive_kind:
    for(i = 0 ; i < m->v.Interactive.body->size ; i++)
      check_stmt(m->v.Interactive.body->elements[i], &global, &ignore);
    cglobal = convert_env(global);
    code = getlbl();
    setd((int) &stackstart, esp);
    for(i = 0 ; i < cglobal->size ; i++) pushimm(0);
    for(i = 0 ; i < m->v.Interactive.body->size ; i++)
      compile_stmt(m->v.Interactive.body->elements[i], cglobal, NULL, NULL, 0, 0);
    break;
  case Expression_kind: //lambda???
    setd((int) &stackstart, esp);
    compile_exp(m->v.Expression.body, NULL, 0);
    break;
  case Suite_kind:
    for(i = 0 ; i < m->v.Suite.body->size ; i++)
      check_stmt(m->v.Suite.body->elements[i], &global, &ignore);
    cglobal = convert_env(global);
    code = getlbl();
    setd((int) &stackstart, esp);
    for(i = 0 ; i < cglobal->size ; i++) pushimm(0);
    for(i = 0 ; i < m->v.Suite.body->size ; i++)
      compile_stmt(m->v.Suite.body->elements[i], cglobal, NULL, NULL, 0, 0);
    break;
  }

  if(cglobal && cglobal->size)
    addimm(esp, 4*cglobal->size);
  ret();

  return code;
}
